import DB from "./indexedDB/index.js"; // 操作本地数据库相关方法
import {
  openDB,
  addData,
  getDataByKey,
  cursorGetData,
  getDataByIndex,
  cursorGetDataByIndex,
  updateDB,
  deleteDB,
  cursorDelete,
  closeDB,
  deleteDBAll,
  cursorGetDataByIndexAndPage,
} from "./indexedDB/indexedDB";
import { axiosCreate } from "./http/index"; // 请求文件
import { doReceive } from "./receiveMessage/index"; // 处理接收到的消息,返回消息对象
import { startHeart, stopHeart, login, sendToken } from "./utils/index"; // 工具函数
import { pfupload } from "./upload/fileUpload"; // 上传文件
import { sendTextAsync } from "./http/api/send"; // 发送消息相关接口

let websocket;
let flag = true;

// 类，用来将一些常用的方法暴露出去
class PFWebsocket {
  // 构造函数
  constructor(options, callback) {
    this.wsUrl = null; // websocket服务器地址
    this.username = options.username; // 用户名
    this.password = options.password; // 密码
    this.httpUrl = options.httpUrl || null; // 接口请求地址
    this.msgSaveDay = options.msgSaveDay || 7; // 聊天消息保存几天
    this.version = options.version || 1; // 本地数据库版本
    this.DBName = options.DBName || "Message";

    // 1.初始化http请求
    axiosCreate(this.httpUrl);

    // 2.发送登录请求 username、password——返回地址和token
    login(this.username, this.password, (data) => {
      this.wsUrl = data.serverAddress;
      this.token = data.token;
      // 将token存到本地
      localStorage.setItem("token", this.token);
      // 3.连接websocket
      createWebsocket(this.wsUrl);
    });

    // 3.连接websocket
    // createWebsocket(this.wsUrl);

    // 4.打开数据库
    openDB(this.DBName, this.version).then(async (db) => {
      this.db = db;
      // 获取列表消息
      cursorGetData(this.db, "messageList", (data) => {
        callback(data.reverse());
      });
    });
  }

  /******************************发送消息**********************************/
  // 发送文本信息
  async sendText(text) {
    const data = await sendTextAsync(text);
  }
  // // 发送图片消息
  // sendImg(file) {
  //   sendMessage.sendImg(file);
  // }
  // // 发送语音消息
  // sendVoice() {
  //   sendMessage.sendVoice();
  // }
  // // 发送视频消息
  // sendVideo() {
  //   sendMessage.sendVideo();
  // }
  // // 发送文件消息
  // sendFile() {
  //   sendMessage.sendFile();
  // }
  // 上传文件到minio服务器
  pfUpload(file, callback) {
    pfupload(file, (data) => {
      if (data) {
        callback(data);
      } else {
        callback(null);
      }
    });
  }

  /******************************查询数据**********************************/
  // 通过索引读取数据
  getDataByIndex() {
    getDataByIndex(this.db, "signalChat", "link", "from:to");
  }
  // 分页查询
  cursorGetDataByIndexAndPage() {
    cursorGetDataByIndexAndPage(
      this.db,
      "signalChat",
      "link",
      "from:to",
      2,
      10,
      (data) => {
        if (data) {
          console.log("分页查询结果", data);
        }
      }
    );
  }
  // 查询消息列表
  queryMessageList(callback) {
    cursorGetData(this.db, "messageList", (data) => {
      callback(data.reverse());
    });
  }
  // 查询本地所有历史消息
  async queryLocalAllData() {
    cursorGetData(this.db, "signalChat");
  }

  // 查询本地某一聊天窗口的所有历史消息
  queryLocalWindowData(key, callback) {
    DB.queryLocalWindowData(key, (data) => {
      callback(data);
    });
  }

  // 删除本地n天前的所有数据
  deleteHistoryData(n) {
    DB.deleteHistoryData(n);
  }
  // 查询服务器所有历史消息
  queryServeAllData() {}
  // 查询服务器某一窗口某一时间段的历史消息
  queryServeWindowData() {}
  // 查询某一窗口的历史消息,如果本地未查询出结果，则在查询服务端
  queryWindowData(key, callback) {
    // 1.先查询本地数据
    // 2.如果本地没有数据，则查询服务端
    DB.queryLocalWindowData(key, (data) => {
      if (data) {
        if (data.length) {
          callback(data);
        } else {
          // 本地未查询到数据，则去服务端查询
          this.queryServeWindowData();
        }
      }
    });
  }

  /******************************删除历史消息**********************************/
  // 删除本地某一条消息
  deleteLocalOneData() {}
  // 删除本地所有历史消息
  deleteLocalAllData() {
    DB.deleteAll();
  }
  // 删除本地某一聊天窗口的所有历史消息
  deleteLocalWindowData(key) {
    DB.deleteAllById(key);
  }
  // 删除服务器所有历史消息
  deleteServeAllData() {}
  // 删除服务器某一窗口的聊天消息
  deleteServeWindowAllData() {}

  /******************************接收消息**********************************/
  // 当收到来自服务器的消息时被调用
  onmessage(callback) {
    websocket.onmessage = async (evt) => {
      // 1.返回解析后的消息
      const dataObj = await doReceive(evt.data);
      let messageType = data.messageType; // 消息类型
      // 需要存储的消息类型数组
      let dealTypeArr = [2, 3, 4, 5, 6, 7, 8];

      if (dealTypeArr.indexOf(messageType) !== -1) {
        // 如果是正常的消息，判断是否在本地通讯录里面

        // 2.将消息存入本地数据库
        addData(this.db, "signalChat", dataObj, (data) => {
          if (data) {
            console.log("数据写入本地成功！");
            // 3.更新消息列表
            updateMessageList(this.db, "messageList", dataObj, () => {
              // 消息列表更新成功后将消息回调出去
              callback(dataObj);
            });
          } else {
            console.log("数据写入本地失败！");
            return;
          }
        });
      } else if (messageType === 14 || messageType === 15) {
        console.log("心跳消息");
        return;
      } else if (messageType === 1) {
        // 登录消息
        callback(dataObj);
      } else {
        return;
      }

      // 4.关闭心跳
      stopHeart();

      // 5.开启心跳
      startHeart(websocket);
    };
  }

  /******************************保存消息**********************************/
  // 将接收的消息保存到本地
  saveMessage(storeName, data) {
    addData(this.db, storeName, data); // 往数据库插入数据
  }

  /******************************关系处理**********************************/
  // 更新本地通讯录
  updateAddressBook() {}

  /******************************本地数据库操作**********************************/
  // 打开数据库
  connectDB() {
    openDB(this.DBName, this.version); // 创建数据库或者打开数据库
  }
  // 删除数据库
  deleteDB(dbName) {
    deleteDBAll(dbName);
  }
  // 关闭数据库
  closeDB() {
    closeDB(this.db);
  }

  /******************************websocket操作**********************************/
  // 关闭websocket连接
  close() {
    stopHeart(); // 关闭心跳
    websocket.close(); // 关闭socket连接
    this.closeDB(); // 关闭数据库
  }
  // websocket关闭后执行函数
  onclose(callback) {
    websocket.onclose = function (evt) {
      stopHeart(); // 关闭心跳
      console.log(
        "websocket 断开: " + evt.code + " " + evt.reason + " " + evt.wasClean
      );
      callback(evt);
    };
  }
  // websocket连接失败函数
  onerror(callback) {
    websocket.onerror = function (evt) {
      callback(evt);
    };
  }
  // websocket连接成功函数
  onopen(callback) {
    websocket.onopen = function (evt) {
      // 1.连接成功60秒内发送一条消息：token
      sendToken(websocket, this.token, 12);
      // 2.发送一条http请求，查看是否有未收到的消息

      // 3.开始心跳
      startHeart(websocket);

      // 3.回调出去，连接成功
      callback(evt);
    };
  }
}

//因为重连函数会被socket事件频繁触发，所以通过函数节流限制重连请求发送
function reConnect(wsUrl) {
  if (!flag) {
    return;
  }
  flag = false;
  //没连接上会一直重连，设置延迟避免请求过多
  setTimeout(function () {
    console.log("重新连接");
    createWebsocket(wsUrl);
    flag = true;
  }, 3000);
}

//建立websocket连接函数
function createWebsocket(wsUrl) {
  try {
    websocket = new WebSocket(wsUrl);
    websocket.binaryType = "arraybuffer"; // 采用字节流的方式传输
  } catch (e) {
    //进行重连;
    console.log("websocket连接错误", e);
    reConnect(wsUrl);
  }
}

// 更新消息列表
function updateMessageList(db, storeName, data, callback) {
  let dataObj = data;
  addData(db, storeName, dataObj, (data) => {
    if (data) {
      // 插入成功
      callback(data);
    } else {
      // 1.先删除这条数据
      deleteDB(db, storeName, dataObj.link, (data) => {
        if (data) {
          // 从新更新消息列表
          updateMessageList(db, storeName, dataObj, (data) => {
            if (data) {
              console.log("更新成功");
            }
          });
        }
      });
    }
  });
}

export default PFWebsocket;

/*
  所有方法定义：
  1.发送消息
    文字:sendText()
    图片:sendImg()
    语音:sendVoice()
    视频:sendVideo()
    文件:sendFile()

  2.查询历史消息
    查询本地所有历史消息: queryLocalAllData()
    查询本地某一聊天窗口的所有历史消息: queryLocalWindowData()
    查询服务器所有历史消息: queryServeAllData()
    查询服务器某一窗口某一时间段的历史消息: queryServeWindowData()
    查询某一窗口的历史消息,如果本地为查询出结果，则在查询服务端: queryWindowData()

  3.删除历史消息
    删除本地某一条消息: deleteLocalOneData()
    删除本地所有历史消息: deleteLocalAllData()
    删除本地某一聊天窗口的所有历史消息: deleteLocalWindowData()
    删除服务器所有历史消息: deleteServeAllData()
    删除服务器某一窗口的聊天消息: deleteServeWindowAllData()
  
  4.接收消息
    接收服务端发送的消息: receiveMessage()
  
  5.保存消息
    将接收的消息保存到本地: saveMessage()
  
  6.本地数据库操作
    连接或创建数据库: connectDB()
    删除数据库: deleteDB()
    关闭数据库: closeDB()

  7.websocket操作
    关闭websocket连接: close()
    websocket连接成功函数: onopen()
    websocket连接失败函数: onerror()
    websocket关闭后执行函数: onclose()
*/
